package simulation;

import java.awt.Color;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.LineBorder;

import operations.OfflineMovement;
import operations.OnlineMovement;
import operations.Operations;
import beans.Agent;
import beans.Cell;
import beans.Meeting;
import beans.State;
import beans.TransDirection;

public class ForTheoryGUI implements ActionListener {

	// GUI parameters
	JFrame guiFrame = new JFrame();
	Container pane = guiFrame.getContentPane();
	JButton[][] grid = new JButton[101][101];
	ImageIcon imgpause = new ImageIcon("images/pause.png");
	ImageIcon imgresume = new ImageIcon("images/resume.png");
	ImageIcon imgstop = new ImageIcon("images/stop.png");
	JButton pause = new JButton(imgpause);
	JButton resume = new JButton(imgresume);
	JButton stop = new JButton(imgstop);

	// WAIT parameters
	public final static int INTERVAL = 50;
	Timer timer;

	static int clock = 0;

	// Simulation Parameters
	private Cell food;
	private List<Agent> lstAgents;
	private List<Cell> nestToFood;
	private List<Cell> snapshot;
	private Cell origin = new Cell(0, 50);

	// FOOD parameters
	private final static int FOOD = 10;
	private int foodAmtAtSource = FOOD;
	private int foodAmtAtNest = 0;

	// MEETING parameters
	private List<Meeting> meetings;
	private static int meetingID=1;

	public ForTheoryGUI(int numAgents, List<Cell> locations) {
		this.food = new Cell(99,50);
		this.lstAgents = new ArrayList<Agent>();
		for (int i = 0; i < numAgents; i++) {
			lstAgents.add(new Agent(i, locations.get(i)));
		}
		this.nestToFood = Operations.getValidPath(origin, food);
		this.meetings = new ArrayList<Meeting>();
		start();
	}

	public void start() {
		setFrameProperties();
		addGridToFrame();
		highlightPathFromNestToFood();
		highlightAgents();
		addControlsToBoard();
		runTheSimulationPeriodically();
	}

	private void highlightPathFromNestToFood() {
		for (Cell c : nestToFood) {
			grid[c.getX()][c.getY()].setBackground(Color.YELLOW);
		}
	}

	private void setFrameProperties() {
		guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		guiFrame.setTitle("Pattern Formation");
		guiFrame.setSize(1045, 1150);
		guiFrame.setLocationRelativeTo(null);
		guiFrame.setResizable(false);
		guiFrame.setAlwaysOnTop(true);
		guiFrame.setLayout(null);
	}

	private void addGridToFrame() {
		JPanel btnPanel = new JPanel();
		btnPanel.setVisible(true);
		btnPanel.setLayout(new GridLayout(101, 101));

		for (int j = 0; j < 101; j++) {
			for (int i = 0; i < 101; i++) {
				grid[i][j] = new JButton();
				grid[i][j].setBackground(Color.WHITE);
				btnPanel.add(grid[i][j]);
			}
		}

		btnPanel.setBounds(10, 100, 1010, 1010);
		pane.add(btnPanel);
		guiFrame.setVisible(true);
	}

	private void addControlsToBoard() {
		pause.setBounds(210, 25, 200, 50);
		resume.setBounds(420, 25, 200, 50);
		stop.setBounds(630, 25, 200, 50);

		resume.setEnabled(false);

		pause.addActionListener(this);
		resume.addActionListener(this);
		stop.addActionListener(this);

		pane.add(pause);
		pane.add(resume);
		pane.add(stop);
	}

	private void runTheSimulationPeriodically() {
		timer = new Timer(INTERVAL, new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				if (foodAmtAtNest == FOOD) {
					System.out.println("Food completely transported at timestep: " + clock);
					System.exit(1);
				}
				performAction();
			}
		});
		timer.start();
	}

	protected void performAction() {
		clock++;
		updateSnapshot();
		exchangeFood();
		lowlightAgents();
		for (Agent ag : lstAgents) {
			ag.setState(decideState(ag));
			ag.setPosition(decideMovement(ag));
		}
		highlightPathFromNestToFood();
		highlightAgents();
	}

	private void exchangeFood() {
		Agent holder;
		Meeting meet;
		
		for (int i = 0; i < lstAgents.size(); i++) {
			holder = lstAgents.get(i);
			if (holder.getPosition().equals(food) && holder.getFoodInPossession() == 0 && foodAmtAtSource > 0) {
				foodAmtAtSource--;
				holder.setFoodInPossession(1);
			} else if (holder.getPosition().equals(origin) && holder.getFoodInPossession() > 0) {
				foodAmtAtNest = foodAmtAtNest + 1;
				holder.setFoodInPossession(0);
			} else if (holder.getFoodInPossession() > 0) {
				int index = getCurrentIndex(holder);
				if (index != 0 && index < nestToFood.size()-1) {
					Cell n1 = nestToFood.get(index - 1);
					Cell n2 = nestToFood.get(index + 1);
					Cell closeToNest = (Operations.getDistance(n1, origin) < Operations.getDistance(n2, origin)) ? n1 : n2;
					int adj = anyAgentAt(closeToNest);
					if (adj >= 0) {
						Agent ex = lstAgents.get(adj);
						if (ex.getFoodInPossession() == 0) {
							holder.setFoodInPossession(0);
							ex.setFoodInPossession(1);
							meet = new Meeting(meetingID++,holder.getId(), ex.getId(), holder.getPosition(), ex.getPosition(), clock, nestToFood);
							//System.out.println(clock + " [Food exchanged from " + holder.getId() + " to " + ex.getId() + "]   " + meet);
							System.out.println(meet);
							meetings.add(meet);
							lstAgents.set(adj, ex);
						}
					}
				}
			}
			lstAgents.set(i, holder);
		}
	}

	private int anyAgentAt(Cell closeToNest) {
		for (Agent a : lstAgents) {
			if (a.getPosition().equals(closeToNest))
				return a.getId();
		}
		return -1;
	}

	private int getCurrentIndex(Agent ag) {
		for (int i = 0; i < nestToFood.size(); i++) {
			if (nestToFood.get(i).equals(ag.getPosition()))
				return i;
		}
		return 0;
	}

	private void highlightAgents() {
		Cell c;
		for (Agent a : lstAgents) {
			c = a.getPosition();
			grid[c.getX()][c.getY()].setBackground(Color.RED);
			grid[c.getX()][c.getY()].setBorder(new LineBorder(Color.BLUE, 2));
		}
	}

	private void lowlightAgents() {
		Cell c;
		for (Agent a : lstAgents) {
			c = a.getPosition();
			grid[c.getX()][c.getY()].setBorder(new LineBorder(Color.GRAY, 1));
			grid[c.getX()][c.getY()].setBackground(Color.WHITE);
		}
	}

	private State decideState(Agent ag) {
		if (nestToFood.contains(ag.getPosition()))
			return State.ONLINE;
		else
			return State.OFFLINE;
	}

	private Cell decideMovement(Agent ag) {
		if (ag.getState().equals(State.OFFLINE)) {
			return new OfflineMovement(ag, nestToFood, snapshot).getMove();
		} else {
			ag.setDirection(decideDirection(ag));
			return new OnlineMovement(ag, nestToFood, snapshot).getMove();
		}
	}

	private TransDirection decideDirection(Agent ag) {
		if (ag.getPosition().equals(nestToFood.get(0)))
			return TransDirection.TOWARDS_FOOD;
		if (ag.getPosition().equals(nestToFood.get(nestToFood.size() - 1)))
			return TransDirection.TOWARDS_NEST;
		return ag.getDirection();
	}

	public void printSnapshot() {
		System.out.print(clock + " [");
		for (int i = 0; i < snapshot.size(); i++) {
			System.out.print(i + ":" + snapshot.get(i) + ", ");
		}
		System.out.println("]");
	}

	private void updateSnapshot() {
		snapshot = new ArrayList<Cell>();
		Cell temp;
		for (Agent a : lstAgents) {
			temp = a.getPosition();
			temp.setTimestep(clock);
			snapshot.add(temp);
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == pause) {
			timer.stop();
			pause.setEnabled(false);
			resume.setEnabled(true);
		}
		if (e.getSource() == resume) {
			timer.start();
			pause.setEnabled(true);
			resume.setEnabled(false);
		}
		if (e.getSource() == stop) {
			System.exit(0);
		}
	}

}
